package com.uniview.service.oauth;

import com.uniview.eumn.CommonEnum;
import com.uniview.exception.CommonException;
import com.uniview.utils.*;
import com.uniview.orm.daoX.AuthClientDetailsMapper;
import com.uniview.orm.daoX.AuthRefreshTokenMapper;
import com.uniview.orm.daoX.SysUserMapper;
import com.uniview.orm.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author lux
 * @date: 2019/11/26
 * @desc: 授权服务，对外提供认证信息
 */
@Service
public class OauthService {
    @Autowired
    AuthClientDetailsMapper authClientDetailsMapper;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    AuthRefreshTokenMapper authRefreshTokenMapper;
    @Autowired
    SysUserMapper sysUserMapper;


    /**
     * @desc 获取授权码
     * @param clientId
     * @param scope
     * @param status
     * @param redirectUri
     */
    public String getAuthorizationCode(String clientId,String scope,String status,String redirectUri){
        //根据clientId获取用户id以及该客户端允许的授权范围
        AuthClientDetailsExample authClientDetailsExample = new AuthClientDetailsExample();
        authClientDetailsExample.createCriteria().andClientIdEqualTo(clientId);
        List<AuthClientDetails> authClientDetailsList = authClientDetailsMapper.selectByExample(authClientDetailsExample);
        if(authClientDetailsList.size()!=1){
            throw new CommonException(CommonEnum.CLIENT_ERROR_CODE, CommonEnum.CLIENT_ERROR_MSG);
        }
        //获取用户信息
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(authClientDetailsList.get(0).getCreateUser());
        List<String> scopeDb = Arrays.asList(authClientDetailsList.get(0).getClientScope().split(","));
        List<String> scopeClient = Arrays.asList(scope.split(","));
        if(!scopeDb.contains(scopeClient)){
            throw new CommonException(CommonEnum.CLIENT_ERROR_CODE, CommonEnum.CLIENT_ERROR_MSG);
        }
        //创建AuthorizationCode
        String str =clientId+scope+ CalendarUtils.getDateTime();
        //加密过后的AuthorizationCode
        String authorizationCode= EncryptUtils.md5Hex(str);
        //保存本次请求的授权范围
        redisTemplate.opsForValue().set("scope:"+authorizationCode,scope,10, TimeUnit.MINUTES);
        //保存本次请求所属的用户信息
        redisTemplate.opsForValue().set("user:"+authorizationCode,sysUser,10,TimeUnit.MINUTES);
        String params = "?code=" + authorizationCode;
        if(org.apache.commons.lang3.StringUtils.isNoneBlank(status)){
            params = params + "&status=" + status;
        }
        return redirectUri+params;
    }

    /**
     * @desc 获取token
     * @param clientId
     * @param code
     * @param grantType
     * @param clientSecret
     * @param redirectUri
     */
    public Map<String,Object> getAccessToken(String clientId,String code,String grantType,String clientSecret,String redirectUri){
        //根据clientID获取client详情
        AuthClientDetails authClientDetails = authClientDetailsMapper.selectByPrimaryKey(clientId);
        if(authClientDetails==null){
            throw new CommonException(CommonEnum.CLIENT_NOT_EXSIT_CODE, CommonEnum.CLIENT_NOT_EXSIT_MSG);
        }
        //获取加密后的clientSecret
        String clientSecretEncode = EncryptUtils.md5Hex(clientSecret);
        //校验clientSecret是否正确
       if(!clientSecretEncode.equals(authClientDetails.getClientSecret())){
           throw new CommonException(CommonEnum.SECRET_ERROR_CODE, CommonEnum.SECRET_ERROR_MSG);
       }
       //校验grantType是否支持
        List<String> grantTypes = Arrays.asList(authClientDetails.getGrantTypes().split(","));
        if(!grantTypes.contains(grantType)){
            throw new CommonException(CommonEnum.GRANT_TYPE_ERROR_CODE, CommonEnum.GRANT_TYPE_ERROR_MSG);
        }
        //校验回调地址是否正确
        if(!redirectUri.equals(authClientDetails.getRedirectUri())){
            throw new CommonException(CommonEnum.REDIRECT_URI_ERROR_CODE, CommonEnum.REDIRECT_URI_ERROR_MSG);
        }
        //获取用户权限范围和用户信息
        if(!redisTemplate.hasKey("scope:"+code)||!redisTemplate.hasKey("user:"+code)){
            throw new CommonException(CommonEnum.AUTHORIZATION_CODE_ERROR_CODE, CommonEnum.AUTHORIZATION_CODE_ERROR_MSG);
        }
        String scopeStr = redisTemplate.opsForValue().get("scope:"+code).toString();
        SysUser sysUser=(SysUser)redisTemplate.opsForValue().get("user:"+code);
        //生成accessToken和刷新token
        String accessToken =  EncryptUtils.md5Hex(sysUser.getId()+"token");
        String refreshToken = EncryptUtils.md5Hex(sysUser.getId()+"refreshToken");
        //组成accessToken信息
        Map<String,Object> tokenMap = new HashMap<String,Object>();
        tokenMap.put("userName",sysUser.getUsername());
        tokenMap.put("userId",sysUser.getId());
        tokenMap.put("clientId",authClientDetails.getClientId());
        tokenMap.put("clientName",authClientDetails.getClientName());
        tokenMap.put("scope",scopeStr);
        //根据clientId和userId查询是否存在refreshToken，如果存在则生成token更新
        AuthRefreshTokenExample authRefreshTokenExample = new AuthRefreshTokenExample();
        authRefreshTokenExample.createCriteria().andClientIdEqualTo(clientId)
                .andCreateUserEqualTo(sysUser.getId())
                .andExpiresInGreaterThan(DateUtils.currentTimeMillis());
        List<AuthRefreshToken> authRefreshTokenList = authRefreshTokenMapper.selectByExample(authRefreshTokenExample);
        if(!authRefreshTokenList.isEmpty()||authRefreshTokenList.size()==1){
            //如果存在则生成token更新,生成accessToken,更新
            AuthRefreshToken authRefreshToken = new AuthRefreshToken();
            authRefreshToken.setAccessToken(accessToken);
            authRefreshToken.setId(authRefreshTokenList.get(0).getId());
            authRefreshTokenMapper.updateByPrimaryKeySelective(authRefreshToken);
            //更新redis,设置accessToken的过期时长是10分钟
            redisTemplate.opsForValue().set("accessToken:"+accessToken,tokenMap,1,TimeUnit.DAYS);
        }else if(authRefreshTokenList.isEmpty()){
            //不存在则新增 ,过期的时间戳 将refreshToken和accessToken关联起来
            Long expiresAt = DateUtils.nextDaysSecond(365L, null);
            AuthRefreshToken authRefreshToken = new AuthRefreshToken();
            authRefreshToken.setId(TransSeqGenerator.getId());
            authRefreshToken.setAccessToken(accessToken);
            authRefreshToken.setCreateTime(CalendarUtils.getDateTime());
            authRefreshToken.setRefreshToken(refreshToken);
            authRefreshToken.setCreateUser(sysUser.getId());
            authRefreshToken.setUpdateTime(CalendarUtils.getDateTime());
            authRefreshToken.setUpdateUser(sysUser.getId());
            authRefreshToken.setExpiresIn(expiresAt);
            authRefreshTokenMapper.insertSelective(authRefreshToken);
            redisTemplate.opsForValue().set("accessToken:"+accessToken,tokenMap,1,TimeUnit.DAYS);
        }else {
            //存在且数量大于1不正常
            throw new CommonException(CommonEnum.CLIENT_ERROR_CODE, CommonEnum.CLIENT_ERROR_MSG);
        }
        Map<String,Object> resMap = new HashMap<String,Object>();
        resMap.put("accessToken",accessToken);
        resMap.put("refreshToken",refreshToken);
        resMap.put("expiresIn",redisTemplate.getExpire("accessToken:"+accessToken));
        resMap.put("scope",scopeStr);
        return resMap;
    }

    /**
     * @desc 刷新token
     * @param refreshToken
     */
    public String refreshToken(String refreshToken){
        //校验refreshToken是否过期
        AuthRefreshTokenExample authRefreshTokenExample = new AuthRefreshTokenExample();
        authRefreshTokenExample.createCriteria().andExpiresInGreaterThan(DateUtils.currentTimeMillis())
                .andRefreshTokenEqualTo(refreshToken);
        List<AuthRefreshToken> authRefreshTokenList = authRefreshTokenMapper.selectByExample(authRefreshTokenExample);
        if(authRefreshTokenList.size()!=1){
            throw new CommonException(CommonEnum.REFRESH_TOKEN_ERROR_CODE, CommonEnum.REFRESH_TOKEN_ERROR_MSG);
        }
        AuthRefreshToken authRefreshToken = authRefreshTokenList.get(0);
        //查询accessToken是否过期
        if(redisTemplate.hasKey("accessToken:"+authRefreshToken.getAccessToken())){
            //如果没有过期，直接返回
            return (String) redisTemplate.opsForValue().get("accessToken:"+authRefreshToken.getAccessToken());
        }else{
            //如果已经过期重新生成，并更新刷新token数据表
            String accessToken =  EncryptUtils.md5Hex(authRefreshToken.getCreateUser()+"token");
            //如果存在则生成token更新,生成accessToken,更新
            AuthRefreshToken authUpdate = new AuthRefreshToken();
            authUpdate.setAccessToken(accessToken);
            authUpdate.setId(authRefreshToken.getId());
            authRefreshTokenMapper.updateByPrimaryKeySelective(authUpdate);
            return accessToken;
        }

    }

}
